/*
 *  ckr_spec_equality_comparisons.cpp
 *  ckrSpec
 *
 *  Created by C. Keith Ray on 2006.11.05.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#import "ckr_spec_cpp_specs.h"
#import "ckr_spec_cpp.h"

#include <iostream>
#include <sstream>
#include <vector>
#include <deque>

#include <assert.h>

using namespace std;
using namespace ckr;

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static void int_values_equal_in_should_equal_should_report_nothing();
static void int_values_unequal_in_should_equal_should_report_failure();

static void std_strings_equal_in_should_equal_reports_nothing();
static void	std_strings_unequal_in_should_equal_reports_failure();

static void c_strings_equal_in_should_equal_should_report_nothing();
static void c_strings_unequal_in_should_equal_should_report_failure();

static void c_string_nonnull_equal_null_in_should_equal_should_report_failure();
static void c_string_null_equal_null_in_should_equal_should_report_nothing();

static void true_value_in_should_be_true_should_report_nothing();
static void false_value_in_should_be_true_should_report_failure();

static void false_value_in_should_be_false_should_report_nothing();
static void true_value_in_should_be_false_should_report_failure();

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void run_equality_comparison_specs()
{
	int_values_equal_in_should_equal_should_report_nothing();
	int_values_unequal_in_should_equal_should_report_failure();

	std_strings_equal_in_should_equal_reports_nothing();
	std_strings_unequal_in_should_equal_reports_failure();

	c_strings_equal_in_should_equal_should_report_nothing();
	c_strings_unequal_in_should_equal_should_report_failure();

	c_string_nonnull_equal_null_in_should_equal_should_report_failure();
	c_string_null_equal_null_in_should_equal_should_report_nothing();

	true_value_in_should_be_true_should_report_nothing();
	false_value_in_should_be_true_should_report_failure();

	false_value_in_should_be_false_should_report_nothing();
	true_value_in_should_be_false_should_report_failure();
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

// now to try spec-driving spec-failures
// Since C++ is a hybid language and doesn't have a way to add a method to all objects, I can't
// add the method "should" to all types, where "should" implements "equal", "be", etc'.
//
// Instead, I'm using the syntax   VALUE( <expression> ).SHOULD_EQUAL  for basic types.
// "value_impl" is actually a method of the Context class, but we take advantage of the implied "this->"
// to reduce the syntax.  The method can then create a temporary object that implements "SHOULD_EQUAL"
// etc.
//
// Users of ckr_spec can implement methods like value, and the related temporary objects,
// for their own types in their own subclasses of Context.

DECLARE_SPEC(Int_values_equal_in_should_equal_context);

void Int_values_equal_in_should_equal_context::spec()
{
	VALUE( 12 ).SHOULD_EQUAL( 12 ); // passes - no output.
	
	// VALUE().SHOULD_EQUAL() works for all basic types that support equality, and all object types
	// that implement 'operator()=='.  That includes std::string, though we also have
	// CSTRING_VALUE() to handle C strings (types 'char *' and 'const char*'.)
}

static void int_values_equal_in_should_equal_should_report_nothing()
{
	Int_values_equal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	const std::string expected; // empty string

	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(Int_values_unequal_in_should_equal_context);

void Int_values_unequal_in_should_equal_context::spec()
{
	VALUE( 12 ).SHOULD_EQUAL( 13 ); // intentional failure - fail msg expected.
};

static void int_values_unequal_in_should_equal_should_report_failure()
{
	Int_values_unequal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	const std::string expected( "12 (12) should equal 13 (13) FAILED in Int_values_unequal_in_should_equal_context; line " );
	// on mac gcc, __FILENAME__ produces the entire pathname of the file, which is really long and depends on
	// data outside of this spec (the pathname).

	std::string::size_type foundPos = actual.find( expected, 0 );

	assert( foundPos != std::string::npos );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(Std_string_equal_in_should_equal_context);

void Std_string_equal_in_should_equal_context::spec()
{
	std::string ralph( "Ralph" );
	std::string ralph2( "Ralph" );

	VALUE( ralph ).SHOULD_EQUAL( ralph2 ); // no output expected
}

static void std_strings_equal_in_should_equal_reports_nothing()
{
	Std_string_equal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();
	const std::string expected;

	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(Std_string_unequal_in_should_equal_context);

void Std_string_unequal_in_should_equal_context::spec()
{
	std::string ralph( "Ralph" );
	std::string mary( "Mary" );

	VALUE( ralph ).SHOULD_EQUAL( mary ); // failure msg expected
}

static void std_strings_unequal_in_should_equal_reports_failure()
{
	Std_string_unequal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	const std::string expected( "ralph (Ralph) should equal mary (Mary) FAILED in Std_string_unequal_in_should_equal_context; line " );
	std::string::size_type foundPos = actual.find( expected, 0 );

	assert( foundPos != std::string::npos );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(C_strings_equal_in_should_equal_context);

void C_strings_equal_in_should_equal_context::spec()
{
	char frankArray[] = "Frank";
	char* frankPtr = "Frank";

	CSTRING_VALUE( "Frank" ).SHOULD_EQUAL( frankPtr ); // no output expected
	CSTRING_VALUE( frankArray ).SHOULD_EQUAL( frankPtr ); // no output expected
	CSTRING_VALUE( "Frank" ).SHOULD_EQUAL( frankArray ); // no output expected
}

static void c_strings_equal_in_should_equal_should_report_nothing()
{
	C_strings_equal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	const std::string expected;
	
	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DECLARE_SPEC(C_strings_unequal_in_should_equal_context);

void C_strings_unequal_in_should_equal_context::spec()
{
	char frankArray[] = "Frank";
	char* frankPtr = "Frank";
	char janeArray[] = "Jane";
	char* janePtr = "Jane";

	CSTRING_VALUE( "Frank" ).SHOULD_EQUAL( "Jane" ); // failure msg expected
	CSTRING_VALUE( frankArray ).SHOULD_EQUAL( janeArray ); // failure msg expected
	CSTRING_VALUE( frankPtr ).SHOULD_EQUAL( janePtr ); // failure msg expected
}

static void c_strings_unequal_in_should_equal_should_report_failure()
{
	C_strings_unequal_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	{
		std::string expected1( "\"Frank\" (Frank) should equal \"Jane\" (Jane) FAILED in C_strings_unequal_in_should_equal_context; line " );
		std::string::size_type foundPos1 = actual.find( expected1, 0 );

		assert( foundPos1 != std::string::npos );
	}
	{
		std::string expected2( "frankArray (Frank) should equal janeArray (Jane) FAILED in C_strings_unequal_in_should_equal_context; line " );
		std::string::size_type foundPos2 = actual.find( expected2, 0 );

		assert( foundPos2 != std::string::npos );
	}
	{
		std::string expected3( "frankPtr (Frank) should equal janePtr (Jane) FAILED in C_strings_unequal_in_should_equal_context; line " );
		std::string::size_type foundPos3 = actual.find( expected3, 0 );

		assert( foundPos3 != std::string::npos );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(C_string_nonnull_equal_null_in_should_equal_context);

void C_string_nonnull_equal_null_in_should_equal_context::spec()
{
	char* nullPtr = 0;

	CSTRING_VALUE( "Frank" ).SHOULD_EQUAL( nullPtr ); // failure msg expected
	CSTRING_VALUE( nullPtr ).SHOULD_EQUAL( "Mary" ); // failure msg expected
}

static void c_string_nonnull_equal_null_in_should_equal_should_report_failure()
{
	C_string_nonnull_equal_null_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	{
		std::string expected1( "\"Frank\" (Frank) should equal nullPtr ((NULL POINTER)) FAILED in C_string_nonnull_equal_null_in_should_equal_context; line ");
		std::string::size_type foundPos1 = actual.find( expected1, 0 );

		assert( foundPos1 != std::string::npos );
	}
	{
		std::string expected2( "nullPtr ((NULL POINTER)) should equal \"Mary\" (Mary) FAILED in C_string_nonnull_equal_null_in_should_equal_context; line ");
		std::string::size_type foundPos2 = actual.find( expected2, 0 );

		assert( foundPos2 != std::string::npos );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(C_string_null_equal_null_in_should_equal_context);

void C_string_null_equal_null_in_should_equal_context::spec()
{
	char* nullPtr = 0;

	CSTRING_VALUE( nullPtr ).SHOULD_EQUAL( nullPtr ); // no output expected
}

static void c_string_null_equal_null_in_should_equal_should_report_nothing()
{
	C_string_null_equal_null_in_should_equal_context aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	std::string expected; // empty string

	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(True_value_in_should_be_true);

void True_value_in_should_be_true::spec()
{
	VALUE( true ).SHOULD_BE_TRUE; // passes - no output.
}

static void true_value_in_should_be_true_should_report_nothing()
{
	True_value_in_should_be_true aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	std::string expected; // empty string

	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(False_value_in_should_be_true);

void False_value_in_should_be_true::spec()
{
	VALUE( false ).SHOULD_BE_TRUE; // expected failure output.
}

static void false_value_in_should_be_true_should_report_failure()
{
	False_value_in_should_be_true aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	std::string expected( "false (false) should be true FAILED in False_value_in_should_be_true; line " );
	std::string::size_type foundPos = actual.find( expected, 0 );

	assert( foundPos != std::string::npos );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(False_value_in_should_be_false);

void False_value_in_should_be_false::spec()
{
	VALUE( false ).SHOULD_BE_FALSE; // passes - no output.
}

static void false_value_in_should_be_false_should_report_nothing()
{
	False_value_in_should_be_false aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	std::string expected; // empty string

	assert( expected == actual );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

DECLARE_SPEC(True_value_in_should_be_false);

void True_value_in_should_be_false::spec()
{
	VALUE( true ).SHOULD_BE_FALSE; // expect failure output.
}

static void true_value_in_should_be_false_should_report_failure()
{
	True_value_in_should_be_false aContext;

	RecordingFailuresSpecListener listener;
	aContext.run_spec( & listener );
	std::string actual = listener.logStr();

	std::string expected( "true (true) should be false FAILED in True_value_in_should_be_false; line " );
	std::string::size_type foundPos = actual.find( expected, 0 );

	assert( foundPos != std::string::npos );
}

